/* Copyright 2009
 *
 * This program and the accompanying materials
 * are made available under the terms of the
 * Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at
 *
 * 		http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * Contributors:
 * 	   IBM Corporation - initial API and implementation for JDT/DLTK
 *     Sean W. Quinn - initial adoption for use with PHP from various sources.
 */
package org.eclipse.php.internal.core;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.php.core.IPHPElement;
import org.eclipse.php.core.IPHPModelStatus;
import org.eclipse.php.core.IPHPModelStatusConstants;
import org.eclipse.php.internal.core.util.Messages;
import org.eclipse.php.internal.ui.PHPUiPluginExt;

/**
 * @see IPHPModelStatus
 */

public class PHPModelStatus extends Status implements IPHPModelStatus,
		IPHPModelStatusConstants {

	/**
	 * The elements related to the failure, or <code>null</code> if no elements
	 * are involved.
	 */
	protected IPHPElement[] elements = new IPHPElement[0];

	/**
	 * The path related to the failure, or <code>null</code> if no path is
	 * involved.
	 */
	protected IPath path;

	/**
	 * The <code>String</code> related to the failure, or <code>null</code> if
	 * no <code>String</code> is involved.
	 */
	protected String string;

	/**
	 * Empty children
	 */
	protected final static IStatus[] NO_CHILDREN = new IStatus[] {};
	protected IStatus[] children = NO_CHILDREN;

	/**
	 * Singleton OK object
	 */
	public static final IPHPModelStatus VERIFIED_OK = new PHPModelStatus(OK,
			OK, Messages.status_OK);

	/**
	 * Constructs an PHP model status with no corresponding elements.
	 */
	public PHPModelStatus() {
		// no code for an multi-status
		super(ERROR, PHPUiPluginExt.PLUGIN_ID, 0, "PHPModelStatus", null); //$NON-NLS-1$
	}

	/**
	 * Constructs an PHP model status with no corresponding elements.
	 */
	public PHPModelStatus(int code) {
		super(ERROR, PHPUiPluginExt.PLUGIN_ID, code, "PHPModelStatus", null); //$NON-NLS-1$
		this.elements = PHPElement.NO_ELEMENTS;
	}

	/**
	 * Constructs an PHP model status with the given corresponding elements.
	 */
	public PHPModelStatus(int code, IPHPElement[] elements) {
		super(ERROR, PHPUiPluginExt.PLUGIN_ID, code, "PHPModelStatus", null); //$NON-NLS-1$
		this.elements = elements;
		this.path = null;
	}

	/**
	 * Constructs an PHP model status with no corresponding elements.
	 */
	public PHPModelStatus(int code, String string) {
		this(ERROR, code, string);
	}

	/**
	 * Constructs an PHP model status with no corresponding elements.
	 */
	public PHPModelStatus(int severity, int code, String string) {
		super(severity, PHPUiPluginExt.PLUGIN_ID, code, "PHPModelStatus", null); //$NON-NLS-1$
		this.elements = PHPElement.NO_ELEMENTS;
		this.path = null;
		this.string = string;
	}

	/**
	 * Constructs an PHP model status with no corresponding elements.
	 */
	public PHPModelStatus(int code, Throwable throwable) {
		super(ERROR, PHPUiPluginExt.PLUGIN_ID, code,
				"PHPModelStatus", throwable); //$NON-NLS-1$
		this.elements = PHPElement.NO_ELEMENTS;
	}

	/**
	 * Constructs an PHP model status with no corresponding elements.
	 */
	public PHPModelStatus(int code, IPath path) {
		super(ERROR, PHPUiPluginExt.PLUGIN_ID, code, "PHPModelStatus", null); //$NON-NLS-1$
		this.elements = PHPElement.NO_ELEMENTS;
		this.path = path;
	}

	/**
	 * Constructs an PHP model status with the given corresponding element.
	 */
	public PHPModelStatus(int code, IPHPElement element) {
		this(code, new IPHPElement[] { element });
	}

	/**
	 * Constructs an PHP model status with the given corresponding element and
	 * string
	 */
	public PHPModelStatus(int code, IPHPElement element, String string) {
		this(code, new IPHPElement[] { element });
		this.string = string;
	}

	/**
	 * Constructs an PHP model status with the given corresponding element and
	 * path
	 */
	public PHPModelStatus(int code, IPHPElement element, IPath path) {
		this(code, new IPHPElement[] { element });
		this.path = path;
	}

	/**
	 * Constructs an PHP model status with the given corresponding element, path
	 * and string
	 */
	public PHPModelStatus(int code, IPHPElement element, IPath path,
			String string) {
		this(code, new IPHPElement[] { element });
		this.path = path;
		this.string = string;
	}

	/**
	 * Constructs an PHP model status with the given corresponding element and
	 * path
	 */
	public PHPModelStatus(int severity, int code, IPHPElement element,
			IPath path, String msg) {
		super(severity, PHPUiPluginExt.PLUGIN_ID, code, "PHPModelStatus", null); //$NON-NLS-1$
		this.elements = new IPHPElement[] { element };
		this.path = path;
		this.string = msg;
	}

	/**
	 * Constructs an PHP model status with no corresponding elements.
	 */
	public PHPModelStatus(CoreException coreException) {
		super(ERROR, PHPUiPluginExt.PLUGIN_ID, CORE_EXCEPTION,
				"PHPModelStatus", coreException); //$NON-NLS-1$
		this.elements = PHPElement.NO_ELEMENTS;
	}

	protected int getBits() {
		int severity = 1 << (getCode() % 100 / 33);
		int category = 1 << ((getCode() / 100) + 3);
		return severity | category;
	}

	/**
	 * @see IStatus
	 */
	@Override
	public IStatus[] getChildren() {
		return this.children;
	}

	/**
	 * @see IPHPModelStatus
	 */
	public IPHPElement[] getElements() {
		return this.elements;
	}

	/**
	 * Returns the message that is relevant to the code of this status.
	 */
	@Override
	public String getMessage() {
		Throwable exception = getException();
		if (exception == null) {
			switch (getCode()) {
			// TODO: Add PHP, and DLTK Error Code Handling Here
			}
			return (this.string != null) ? this.string : ""; // $NON-NLS-1$
		} else {
			String message = exception.getMessage();
			return (message != null) ? message : exception.toString();
		}
	}

	/**
	 * @see IPHPModelStatus#getPath()
	 */
	public IPath getPath() {
		return this.path;
	}

	/**
	 * @see IStatus#getSeverity()
	 */
	@Override
	public int getSeverity() {
		if (this.children == NO_CHILDREN)
			return super.getSeverity();
		int severity = -1;
		for (int i = 0, max = this.children.length; i < max; i++) {
			int childrenSeverity = this.children[i].getSeverity();
			if (childrenSeverity > severity) {
				severity = childrenSeverity;
			}
		}
		return severity;
	}

	/**
	 * @see IPHPModelStatus#getString()
	 * @deprecated
	 */
	@Deprecated
	public String getString() {
		return this.string;
	}

	/**
	 * @see IPHPModelStatus#isDoesNotExist()
	 */
	public boolean isDoesNotExist() {
		int code = getCode();
		return code == ELEMENT_DOES_NOT_EXIST
				|| code == ELEMENT_NOT_ON_BUILDPATH;
	}

	/**
	 * @see IStatus#isMultiStatus()
	 */
	@Override
	public boolean isMultiStatus() {
		return this.children != NO_CHILDREN;
	}

	/**
	 * @see IStatus#isOK()
	 */
	@Override
	public boolean isOK() {
		return getCode() == OK;
	}

	/**
	 * @see IStatus#matches(int)
	 */
	@Override
	public boolean matches(int mask) {
		if (!isMultiStatus()) {
			return matches(this, mask);
		} else {
			for (int i = 0, max = this.children.length; i < max; i++) {
				if (matches((PHPModelStatus) this.children[i], mask))
					return true;
			}
			return false;
		}
	}

	/**
	 * Helper for matches(int).
	 */
	protected boolean matches(PHPModelStatus status, int mask) {
		int severityMask = mask & 0x7;
		int categoryMask = mask & ~0x7;
		int bits = status.getBits();
		return ((severityMask == 0) || (bits & severityMask) != 0)
				&& ((categoryMask == 0) || (bits & categoryMask) != 0);
	}

	/**
	 * Creates and returns a new <code>IPHPModelStatus</code> that is a a
	 * multi-status status.
	 *
	 * @see IStatus#isMultiStatus()
	 */
	public static IPHPModelStatus newMultiStatus(IPHPModelStatus[] children) {
		PHPModelStatus jms = new PHPModelStatus();
		jms.children = children;
		return jms;
	}

	/**
	 * Returns a printable representation of this exception for debugging
	 * purposes.
	 */
	@Override
	public String toString() {
		if (this == VERIFIED_OK) {
			return "PHPModelStatus[OK]"; //$NON-NLS-1$
		}
		StringBuffer buffer = new StringBuffer();
		buffer.append("PHP Model Status ["); //$NON-NLS-1$
		buffer.append(getMessage());
		buffer.append("]"); //$NON-NLS-1$
		return buffer.toString();
	}
}
